/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_DECODER)

#include <trace_log.h>
#include "umc_avs_dec_decompressor.h"
#include "umc_avs_sequence_header.h"
#include "umc_avs_picture_header.h"
#include "umc_avs_dec_bit_stream.h"
#include "umc_avs_mb_info.h"
#include "umc_avs_pic.h"
#include "umc_avs_slice.h"

namespace UMC
{

AVSDecompressor::AVSDecompressor(void)
{

} // AVSDecompressor::AVSDecompressor(void)

AVSDecompressor::~AVSDecompressor(void)
{

} // AVSDecompressor::AVSDecompressor(void)

void AVSDecompressor::PrepareNeighbours(AVS_BASE_CONTEXT *pCtx)
{
    // set the current macroblock
    m_pMbInfo = pCtx->m_pMBInfo;

    // set the pointer to the left macroblock
    m_pMbInfoLeft = NULL;

    // set the pointer to the upper macroblocks
    if (pCtx->MbIndex > pCtx->MbFirst)
    {
        m_pMbInfoTop = pCtx->m_pMBInfo - pCtx->MbWidth;
        m_pMbInfoTopRight = (1 < pCtx->MbWidth) ? (m_pMbInfoTop + 1) : (NULL);
    }
    else
    {
        m_pMbInfoTop = NULL;
        m_pMbInfoTopRight = NULL;
    }

    m_pMbInfoTopLeft = NULL;

} // void AVSDecompressor::PrepareNeighbours(AVS_BASE_CONTEXT *pCtx)

void AVSDecompressor::PrepareDecoding(bool isThreaded)
{
    if (AVS_I_PICTURE != m_decCtx.m_pPicHeader->PictureType)
    {
        AVSPicture *pPic = m_decCtx.m_pSlice->m_pPic;

        // move blockDist closer
        memcpy(m_blockDist, pPic->m_blockDist, sizeof(m_blockDist));
        memcpy(m_distIdx, pPic->m_distIdx, sizeof(m_distIdx));

        // move reference list close
        memcpy(m_pRefs, pPic->m_pRefs, sizeof(m_pRefs));
    }

    // advance the coefficient buffer pointer to reserve
    // some space for a piece of additional info
    if (isThreaded)
    {
        m_pExtraInfo = (AVS_DEC_ADDITIONAL_INFO *) m_pWriteCoeffs;
        m_pExtraInfo->m_maxVertOffset = 0;
        m_pWriteCoeffs += (sizeof(AVS_DEC_ADDITIONAL_INFO) / sizeof(m_pWriteCoeffs[0]));
    }

} // void AVSDecompressor::PrepareDecoding(bool isThreaded)

void AVSDecompressor::PrepareReconstruction(bool isThreaded)
{
    if (AVS_I_PICTURE != m_recCtx.m_pPicHeader->PictureType)
    {
        AVSPicture *pPic = m_recCtx.m_pSlice->m_pPic;

        // move reference list close
        memcpy(m_pRefs, pPic->m_pRefs, sizeof(m_pRefs));

        // prepare motion compensation structures
        memset(&m_lumaMCParams, 0, sizeof(m_lumaMCParams));
        m_lumaMCParams.sizeFrame = pPic->m_sizeLuma;

        memset(&m_chromaMCParams, 0, sizeof(m_chromaMCParams));
        m_chromaMCParams.sizeFrame = pPic->m_sizeChroma;

        // move picture's plane pointers closer
        memcpy(m_pPlanes8u, pPic->m_pPlanes8u, sizeof(m_pPlanes8u));
        m_iPitch = pPic->m_iPitch;
    }

    // advance the coefficient buffer pointer to skip
    // some space holding a piece of additional info
    if (isThreaded)
    {
        m_pReadCoeffs += (sizeof(AVS_DEC_ADDITIONAL_INFO) / sizeof(m_pReadCoeffs[0]));
    }

} // void AVSDecompressor::PrepareReconstruction(bool isThreaded)

void AVSDecompressor::ResetMacroblock(void)
{
    // reset macroblock info
    memset(m_pMbInfo, 0, sizeof(AVS_MB_INFO));

} // void AVSDecompressor::ResetMacroblock(void)

static
Ipp32s GetMaximumOffset(Ipp32s vertVector, Ipp32s blockHeight)
{
    // does the vector have sub-pixel partition ?
    if (vertVector & 3)
        blockHeight += 3;
    vertVector >>= 2;

    return (vertVector + blockHeight);

} // Ipp32s GetMaximumOffset(Ipp32s vertVector, Ipp32s blockHeight)

void AVSDecompressor::UpdateAffectedArea(void)
{
    Ipp32s maxVertOffset;
    AVSMVector *pVec = m_pMbInfo->mv[AVS_FORWARD];

    // update maximum forward vector of the slice
    maxVertOffset = GetMaximumOffset(pVec[0].vector.y, 8);
    maxVertOffset = IPP_MAX(maxVertOffset,
                            GetMaximumOffset(pVec[1].vector.y, 8));
    maxVertOffset = IPP_MAX(maxVertOffset,
                            GetMaximumOffset(pVec[2].vector.y, 16));
    maxVertOffset = IPP_MAX(maxVertOffset,
                            GetMaximumOffset(pVec[3].vector.y, 16));

    // update maximum backward vector of the slice
    if (AVS_B_PICTURE == m_decCtx.m_pPicHeader->PictureType)
    {
        pVec = m_pMbInfo->mv[AVS_BACKWARD];

        maxVertOffset = IPP_MAX(maxVertOffset,
                                GetMaximumOffset(pVec[0].vector.y, 8));
        maxVertOffset = IPP_MAX(maxVertOffset,
                                GetMaximumOffset(pVec[1].vector.y, 8));
        maxVertOffset = IPP_MAX(maxVertOffset,
                                GetMaximumOffset(pVec[2].vector.y, 16));
        maxVertOffset = IPP_MAX(maxVertOffset,
                                GetMaximumOffset(pVec[3].vector.y, 16));
    }

    // create maximum offset in pixels
    maxVertOffset = maxVertOffset + m_decCtx.MbY * 16;

    // normalize to frame width
    maxVertOffset = IPP_MIN(maxVertOffset, m_decCtx.MbHeight * 16);

    m_pExtraInfo->m_maxVertOffset = IPP_MAX(m_pExtraInfo->m_maxVertOffset,
                                            maxVertOffset);

} // void AVSDecompressor::UpdateAffectedArea(void)

void AVSDecompressor::AdvanceNeighbours(void)
{
    // update pointer to the upper neighbours
    if (m_pMbInfoTop)
    {
        m_pMbInfoTopLeft = m_pMbInfoTop;

        m_pMbInfoTop += 1;

        m_pMbInfoTopRight = (MbIndex + 1 < MbMax) ?
                            (m_pMbInfoTopRight + 1) :
                            (NULL);
    }

    // update pointer to the left neighbour
    m_pMbInfoLeft = m_pMbInfo;

    // update pointer to the current macroblock
    m_pMbInfo += 1;

} // void AVSDecompressor::AdvanceNeighbours(void)

void AVSDecompressor::AdvanceDecoding(void)
{
    // update macroblock position
    m_decCtx.MbX += 1;

} // void AVSDecompressor::AdvanceDecoding(void)

void AVSDecompressor::AdvanceReconstruction(void)
{
    // update plane pointers
    if (1 == m_recCtx.m_pSeqHeader->sample_precission)
    {
        // advance luminance pointer
        m_recCtx.m_pPlanes8u[0] += 16;

        // advance chrominance pointer
        m_recCtx.m_pPlanes8u[1] += 8;
        m_recCtx.m_pPlanes8u[2] += 8;
    }

    // update macroblock position
    m_recCtx.MbX += 1;

} // void AVSDecompressor::AdvanceReconstruction(void)

void AVSDecompressor::FinalizeDecoding(void)
{
    // update variables
    m_decCtx.MbIndex += m_decCtx.MbWidth;
    m_decCtx.m_pMBInfo += m_decCtx.MbWidth;
    m_decCtx.m_pCoeffs = m_pWriteCoeffs;
    m_decCtx.MbX = 0;
    m_decCtx.MbY = m_decCtx.MbY + 1;

} // void AVSDecompressor::FinalizeDecoding(void)

void AVSDecompressor::FinalizeReconstruction(void)
{
    // update variables
    m_recCtx.MbIndex += m_recCtx.MbWidth;
    m_recCtx.m_pMBInfo += m_recCtx.MbWidth;
    m_recCtx.m_pCoeffs = m_pReadCoeffs;
    m_recCtx.MbX = 0;
    m_recCtx.MbY = m_recCtx.MbY + 1;

    // update pointers to the video planes
    if (1 == m_recCtx.m_pSeqHeader->sample_precission)
    {
        Ipp32s iPitch = m_recCtx.m_iPitch;

        m_recCtx.m_pPlanes8u[0] = m_recCtx.m_pPlanes8u[0] -
                                  m_recCtx.MbWidth * 16 +
                                  iPitch * 16;
        if (AVS_CHROMA_420_FORMAT == m_recCtx.m_pSeqHeader->chroma_format)
        {
            m_recCtx.m_pPlanes8u[1] = m_recCtx.m_pPlanes8u[1] -
                                      m_recCtx.MbWidth * 8 +
                                      iPitch * 8;
            m_recCtx.m_pPlanes8u[2] = m_recCtx.m_pPlanes8u[2] -
                                      m_recCtx.MbWidth * 8 +
                                      iPitch * 8;
        }
        else
        {
            m_recCtx.m_pPlanes8u[1] = m_recCtx.m_pPlanes8u[1] -
                                      m_recCtx.MbWidth * 8 +
                                      iPitch * 16;
            m_recCtx.m_pPlanes8u[2] = m_recCtx.m_pPlanes8u[2] -
                                      m_recCtx.MbWidth * 8 +
                                      iPitch * 16;
        }
    }

} // void AVSDecompressor::FinalizeReconstruction(void)

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_DECODER)
